home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 5 / MacMania 5.toast / / Internet software / NewsWatcher / NW Source / Source / prefs.c < prev    next >
Text File  |  1997-01-09  |  49KB  |  1,640 lines

  1. /*----------------------------------------------------------------------------
  2.  
  3.     prefs.c
  4.  
  5.     This module handles locating, reading, and writing the prefs file, and
  6.     initialization of the preferences if no prefs file exists or an old
  7.     version is encountered.
  8.     
  9.     Copyright © 1994-1997, Northwestern University.
  10.  
  11. ----------------------------------------------------------------------------*/
  12.  
  13. #include <string.h>
  14. #include <stdio.h>
  15.  
  16. #include "glob.h"
  17. #include "prefs.h"
  18. #include "dialog.h"
  19. #include "full.h"
  20. #include "newswatcher.h"
  21. #include "drawutil.h"
  22. #include "strutil.h"
  23. #include "memutil.h"
  24. #include "cache.h"
  25. #include "apputil.h"
  26. #include "wind.h"
  27. #include "fileutil.h"
  28. #include "resutil.h"
  29. #include "menus.h"
  30. #include "sfutil.h"
  31. #include "url.h"
  32. #include "ic.h"
  33. #include "print.h"
  34. #include "header.h"
  35.  
  36.  
  37.  
  38. #define kNewUserDlg            158            /* New user dialog */
  39. #define kNewUserPrivateItem    1
  40. #define    kNewUserSharedItem    2
  41. #define kNewUserLabItem        3
  42. #define kNewUserQuitItem    4
  43.  
  44. #define kGetFloppyDlg                        159            /* Get floppy dialog */
  45. #define kGetFloppyCancelItem                1
  46.  
  47. #define kEraseFloppyDlg                        160            /* Erase floppy dialog */
  48. #define kEraseFloppyCancelItem                2
  49.  
  50. #define kWaitEraseDlg                        161            /* Wait for floppy erase dialog */
  51.  
  52. #define kServerDlg            132            /* Server dialog */
  53. #define kNewsServer            6
  54. #define kMailServer            8
  55.  
  56. #define kPersonalDlg        138            /* Personal info dialog */
  57. #define kFullName            6
  58. #define kOrganization        8
  59. #define kAddress            10
  60.  
  61. #define kNew20UserAlert        146
  62.  
  63. #define kSavedMsgDefaultFolderAliasID        128        /* 'alis' resource id for saved message
  64.                                                        default folder alias */
  65. #define kSavedArtDefaultFolderAliasID        129        /* 'alis' resource id for saved article
  66.                                                        default folder alias */
  67. #define ksavedBinDefaultFolderAliasID        130        /* 'alis' resource id for extracting
  68.                                                        binaries download folder alias */
  69. #define ksavedUGLDefaultFolderAliasID        131        /* 'alis' resource id for saved user
  70.                                                         group list folder alias */
  71.  
  72. #define kURLHelpersType                        'URLH'    /* rsrc type for URL helpers */
  73. #define kURLHelpersID                        128        /* rsrc id for URL helpers */
  74.  
  75.  
  76.  
  77. static AliasHandle gPrefsAlias = nil;    /* handle to alias to prefs file, or
  78.                                            nil if prefs file not found */
  79. static Boolean gPrefsFileWasOldVersion = false;
  80.                                         /* true if prefs file was old version */
  81. static Str255 gNewsServerAtStartup;        /* news server address at startup */
  82. static Boolean gDummyPrefsFile = false;    /* true if dummy prefs file found */
  83. static Boolean gDummyPrefsLabFile = false;    /* true if dummy prefs file contains "lab" */
  84. static Boolean gFloppyWasFormatted;        /* true if floppy was formatted on insertion */
  85.  
  86.  
  87.  
  88. /*----------------------------------------------------------------------------
  89.     MakeDefaultFolderAlias 
  90.     
  91.     Initialize the alias for a default folder preference to the desktop
  92.     folder on the startup volume.
  93.     
  94.     Entry:    *alias = handle to previous alias if any, else nil.
  95.             
  96.     Exit:    *alias = handle to new alias.
  97. ----------------------------------------------------------------------------*/
  98.  
  99. static void MakeDefaultFolderAlias (AliasHandle *alias)
  100. {
  101.     OSErr err = noErr;
  102.     FSSpec fSpec;
  103.     CInfoPBRec pBlock;
  104.  
  105.     if (*alias != nil) {
  106.         MyDisposeHandle(*alias);
  107.         *alias = nil;
  108.     }
  109.     err = FindFolder(kOnSystemDisk, kDesktopFolderType, false,
  110.         &pBlock.dirInfo.ioVRefNum, &pBlock.dirInfo.ioDrDirID);
  111.     if (err != noErr) return;
  112.     pBlock.dirInfo.ioNamePtr = fSpec.name;
  113.     pBlock.dirInfo.ioFDirIndex = -1;
  114.     err = PBGetCatInfoSync(&pBlock);
  115.     if (err != noErr) return;
  116.     fSpec.vRefNum = pBlock.dirInfo.ioVRefNum;
  117.     fSpec.parID = pBlock.dirInfo.ioDrParID;
  118.     NewAlias(nil, &fSpec, alias);
  119. }
  120.  
  121.  
  122.  
  123. /*----------------------------------------------------------------------------
  124.     GetFloppyDialogFilter
  125.     
  126.     Filter proc for the get floppy dialog.
  127.         
  128.     Entry:    dlg = pointer to dialog.
  129.             ev = pointer to event record.
  130.             
  131.     Exit:    function result = true if event handled and item hit.
  132.             *itemHit = item number of item hit.
  133.             ev = pointer to possibly modified event record.
  134.             
  135.     When a floppy is inserted, *itemHit is set to the negative of the drive
  136.     number of the inserted floppy, and the function returns true. In addition,
  137.     the global variable gFloppyWasFormatted is set to true if the floppy was
  138.     formatted.
  139. ----------------------------------------------------------------------------*/
  140.  
  141. static pascal Boolean GetFloppyDialogFilter (DialogPtr dlg, EventRecord *ev, 
  142.     short *itemHit)
  143. {
  144.     Point thisPoint;
  145.     OSErr err = noErr;
  146.  
  147.     if (ev->what == diskEvt) {
  148.         if ((ev->message & 0xffff0000) != 0) {
  149.             DILoad();
  150.             SetPt(&thisPoint, 120, 120);
  151.             err = DIBadMount(thisPoint, ev->message);
  152.             DIUnload();
  153.             if (err != noErr) return err;
  154.             gFloppyWasFormatted = true;
  155.         }
  156.         *itemHit = -(ev->message & 0xffff);
  157.         return true;
  158.     }
  159.     return DialogFilter(dlg, ev, itemHit);
  160. }
  161.  
  162.  
  163.  
  164. /*----------------------------------------------------------------------------
  165.     DoGetFloppyDialog
  166.     
  167.     Present the get floppy dialog.
  168.     
  169.     Exit:    function result = error code.
  170.             *vRefNum = vol ref num of inserted floppy.
  171.             name = vol name.
  172. ----------------------------------------------------------------------------*/
  173.  
  174. static OSErr DoGetFloppyDialog (short *vRefNum, StringPtr name)
  175. {
  176.     DialogPtr dlg = nil;
  177.     short item, drvNum;
  178.     long freeBytes;
  179.     HParamBlockRec pBlock;
  180.     OSErr err = noErr;
  181.  
  182.     UnmountVol(nil, 1);
  183.     Eject(nil, 1);
  184.     UnmountVol(nil, 2);
  185.     Eject(nil, 2);
  186.     
  187.     while (true) {
  188.         gFloppyWasFormatted = false;
  189.         err = MyGetNewDialog(kGetFloppyDlg, 0, kGetFloppyCancelItem, &dlg);
  190.         if (err != noErr) return err;
  191.         MyMovableModalDialog(dlg, GetFloppyDialogFilter, &item);
  192.         err = DoClose(dlg);
  193.         if (err != noErr) return err;
  194.         if (item == kGetFloppyCancelItem) return userCanceledErr;
  195.         drvNum = -item;
  196.         err = GetVInfo(drvNum, name, vRefNum, &freeBytes);
  197.         if (err != noErr) return err;
  198.         pBlock.volumeParam.ioNamePtr = name;
  199.         pBlock.volumeParam.ioVRefNum = drvNum;
  200.         pBlock.volumeParam.ioVolIndex = 0;
  201.         err = PBHGetVInfoSync(&pBlock);
  202.         if (err != noErr) return err;
  203.         if ((pBlock.volumeParam.ioVAtrb >> 7) & 1) {
  204.             UnmountVol(nil, drvNum);
  205.             Eject(nil, drvNum);
  206.             ErrorMessageNumber(kStrFloppyLocked);
  207.             continue;
  208.         }
  209.         if (!gFloppyWasFormatted) {
  210.             err = MyGetNewDialog(kEraseFloppyDlg, kEraseFloppyCancelItem,
  211.                 kEraseFloppyCancelItem, &dlg);
  212.             ParamText(name, "\p", "\p", "\p");
  213.             if (err != noErr) return err;
  214.             MyMovableModalDialog(dlg, DialogFilter, &item);
  215.             err = DoClose(dlg);
  216.             if (err != noErr) return err;
  217.             if (item == kEraseFloppyCancelItem) {
  218.                 UnmountVol(nil, drvNum);
  219.                 Eject(nil, drvNum);
  220.                 continue;
  221.             }
  222.             err = MyGetNewDialog(kWaitEraseDlg, 0, 0, &dlg);
  223.             if (err != noErr) return err;
  224.             SetCursor(&gWatchCurs);
  225.             MyShowWindow(dlg);
  226.             HandleUpdate(dlg);
  227.             err = UnmountVol(nil, drvNum);
  228.             if (err == noErr) err = DIFormat(drvNum);
  229.             if (err == noErr) err = DIVerify(drvNum);
  230.             if (err == noErr) err = DIZero(drvNum, name);
  231.             DoClose(dlg);
  232.             SetCursor(&qd.arrow);
  233.             FlushEvents(everyEvent,0);
  234.             if (err != noErr) {
  235.                 UnmountVol(nil, drvNum);
  236.                 Eject(nil, drvNum);
  237.                 ErrorMessageNumber(kStrFloppyEraseFailed);
  238.                 continue;
  239.             }
  240.         }
  241.         break;
  242.     }
  243.  
  244.     return noErr;
  245. }
  246.  
  247.  
  248.  
  249. /*----------------------------------------------------------------------------
  250.     CreateSharedOrLabPrefsFile
  251.     
  252.     Create the initial prefs file in a personal NW folder or on a lab floppy.
  253.     
  254.     Entry:    fSpec = pointer to file spec for personal NW folder or floppy disk.
  255.             dirID = directory id of personal NW folder or floppy disk.
  256.     
  257.     Exit:    function result = error code.
  258.             *nwFolderAlias = Handle to alias to personal NW folder or floppy disk.
  259.             gPrefsAlias = Handle to alias to prefs file.
  260. ----------------------------------------------------------------------------*/
  261.  
  262. static OSErr CreateSharedOrLabPrefsFile (FSSpec *fSpec, long dirID,
  263.     AliasHandle *nwFolderAlias)
  264. {
  265.     FSSpec prefs;
  266.     OSErr err = noErr;
  267.  
  268.     err = NewAlias(nil, fSpec, nwFolderAlias);
  269.     if (err != noErr) return err;
  270.     prefs.vRefNum = fSpec->vRefNum;
  271.     prefs.parID = dirID;
  272.     GetPString(kStrPrefsFileName, prefs.name);
  273.     err = MyFSpCreateResFile(&prefs, kNewsWatcherSignature, 
  274.         kPrefsFileType, smSystemScript);
  275.     if (err != noErr) return err;
  276.     return NewAlias(nil, &prefs, &gPrefsAlias);
  277. }
  278.  
  279.  
  280.  
  281. /*----------------------------------------------------------------------------
  282.     DoNewUserDialog
  283.     
  284.     Present the new user dialog.
  285.     
  286.     Exit:    function result = error code.
  287.             *nwFolderAlias = nil if private Mac, else handle to alias to 
  288.                 personal NW folder or personal NW floppy disk.
  289. ----------------------------------------------------------------------------*/
  290.  
  291. static OSErr DoNewUserDialog (AliasHandle *nwFolderAlias)
  292. {
  293.     DialogPtr dlg = nil;
  294.     short item;
  295.     StandardFileReply reply;
  296.     Str255 string;
  297.     AliasHandle alias;
  298.     long dirID;
  299.     FSSpec fSpec;
  300.     OSErr err = noErr;
  301.  
  302.     while (true) {
  303.     
  304.         err = MyGetNewDialog(kNewUserDlg, 0, 0, &dlg);
  305.         if (err != noErr) return err;
  306.         SetItemKeyEquivalent(dlg, kNewUserPrivateItem, 'P');
  307.         SetItemKeyEquivalent(dlg, kNewUserSharedItem, 'S');
  308.         SetItemKeyEquivalent(dlg, kNewUserLabItem, 'L');
  309.         SetItemKeyEquivalent(dlg, kNewUserQuitItem, 'Q');
  310.         if (gDummyPrefsFile) DlgEnableItem(dlg, kNewUserPrivateItem, false);
  311.         if (gDummyPrefsLabFile) DlgEnableItem(dlg, kNewUserSharedItem, false);
  312.         MyMovableModalDialog(dlg, DialogFilter, &item);
  313.         err = DoClose(dlg);
  314.         if (err != noErr) return err;
  315.     
  316.         switch (item) {
  317.             case kNewUserPrivateItem:
  318.                 *nwFolderAlias = nil;
  319.                 return noErr;
  320.             case kNewUserSharedItem:
  321.                 GetPString(kStrPersonalNWFolder, string);
  322.                 alias = nil;
  323.                 MakeDefaultFolderAlias(&alias);
  324.                 MyStandardPutFile(string, "\p", &reply, alias);
  325.                 MyDisposeHandle(alias);
  326.                 alias = nil;
  327.                 if (!reply.sfGood) break;
  328.                 err = FSpDirCreate(&reply.sfFile, reply.sfScript, &dirID);
  329.                 if (err != noErr) return err;
  330.                 return CreateSharedOrLabPrefsFile(&reply.sfFile, dirID, nwFolderAlias);
  331.             case kNewUserLabItem:
  332.                 err = DoGetFloppyDialog(&fSpec.vRefNum, fSpec.name);
  333.                 if (err == userCanceledErr) break;
  334.                 if (err != noErr) return err;
  335.                 fSpec.parID = fsRtParID;
  336.                 return CreateSharedOrLabPrefsFile(&fSpec, fsRtDirID, nwFolderAlias);
  337.                 break;
  338.             case kNewUserQuitItem:
  339.                 ExitToShell();
  340.         }
  341.     }
  342.     
  343.     return noErr;
  344. }
  345.  
  346.  
  347.  
  348. /*----------------------------------------------------------------------------
  349.     DoServerDialog 
  350.     
  351.     Present the server dialog.
  352.     
  353.     Exit:    function result = error code.
  354.             gPrefs.newsServerName = news server name.
  355.             gPrefs.mailServerName = mail server name.
  356. ----------------------------------------------------------------------------*/
  357.  
  358. static OSErr DoServerDialog (void)
  359. {
  360.     DialogPtr dlg = nil;
  361.     short item;
  362.     Str255 news;
  363.     Str255 mail;
  364.     OSErr err = noErr;
  365.         
  366.     err = MyGetNewDialog(kServerDlg, ok, cancel, &dlg);
  367.     if (err != noErr) return err;
  368.     CopyPascalString(news, gPrefs.newsServerName);
  369.     DlgSetPString(dlg, kNewsServer, news);
  370.     SetItemHostAddress(dlg, kNewsServer);
  371.     SetItemMaxLength(dlg, kNewsServer, 255);
  372.     CopyPascalString(mail, gPrefs.mailServerName);
  373.     DlgSetPString(dlg, kMailServer, mail);
  374.     SetItemHostAddress(dlg, kMailServer);
  375.     SetItemMaxLength(dlg, kMailServer, 255);
  376.     if (*news == 0) {
  377.         SelectDialogItemText(dlg, kNewsServer, 0, 0);
  378.     } else if (*mail == 0) {
  379.         SelectDialogItemText(dlg, kMailServer, 0, 0);
  380.     } else {
  381.         SelectDialogItemText(dlg, kNewsServer, 0, kMaxShort);
  382.     }
  383.     
  384.     do {
  385.         DlgEnableItem(dlg, ok, *news != 0 && *mail != 0);
  386.         MyMovableModalDialog(dlg, DialogFilter, &item);
  387.         switch (item) {
  388.             case kNewsServer:
  389.                 DlgGetPString(dlg, item, news);
  390.                 break;
  391.             case kMailServer:
  392.                 DlgGetPString(dlg, item, mail);
  393.                 break;
  394.         }
  395.     } while (item != ok && item != cancel);
  396.  
  397.     err = DoClose(dlg);
  398.     if (err != noErr) return err;
  399.     if (item == ok) {
  400.         CopyPascalString(gPrefs.newsServerName, news);
  401.         CopyPascalString(gPrefs.mailServerName, mail);
  402.         MyICWriteSharedPrefs(kICNNTPHost);
  403.         MyICWriteSharedPrefs(kICSMTPHost);
  404.     }
  405.     return item == ok ? noErr : userCanceledErr;
  406. }
  407.  
  408.  
  409.  
  410. /*----------------------------------------------------------------------------
  411.     DoPersonalDialog 
  412.     
  413.     Present the personal information dialog.
  414.     
  415.     Exit:    function result = error code.
  416.             gPrefs.fullName = user's full name.
  417.             gPrefs.organization = user's organization.
  418.             gPrefs.emailAddress = user's email address
  419. ----------------------------------------------------------------------------*/
  420.  
  421. static OSErr DoPersonalDialog (void)
  422. {
  423.     DialogPtr dlg = nil;
  424.     short item;
  425.     CStr255 fullName;
  426.     CStr255    organization;
  427.     CStr255 address;
  428.     OSErr err = noErr;
  429.  
  430.     err = MyGetNewDialog(kPersonalDlg, ok, cancel, &dlg);
  431.     if (err != noErr) return err;
  432.     strcpy(fullName, gPrefs.fullName);
  433.     DlgSetCString(dlg, kFullName, fullName);
  434.     SetItemMaxLength(dlg, kFullName, 255);
  435.     strcpy(organization, gPrefs.organization);
  436.     DlgSetCString(dlg, kOrganization, organization);
  437.     SetItemMaxLength(dlg, kOrganization, 255);
  438.     strcpy(address, gPrefs.emailAddress);
  439.     DlgSetCString(dlg, kAddress, address);
  440.     SetItemUSAsciiNoBlank(dlg, kAddress);
  441.     SetItemMaxLength(dlg, kAddress, 255);
  442.     if (*fullName == 0) {
  443.         SelectDialogItemText(dlg, kFullName, 0, 0);
  444.     } else if (*organization == 0) {
  445.         SelectDialogItemText(dlg, kOrganization, 0, 0);
  446.     } else if (*address == 0) {
  447.         SelectDialogItemText(dlg, kAddress, 0, 0);
  448.     } else {
  449.         SelectDialogItemText(dlg, kFullName, 0, kMaxShort);
  450.     }
  451.     
  452.     while (true) {
  453.         DlgEnableItem(dlg, ok, *address != 0);
  454.         MyMovableModalDialog(dlg, DialogFilter, &item);
  455.         switch (item) {
  456.             case kFullName:
  457.                 DlgGetCString(dlg, item, fullName);
  458.                 break;
  459.             case kOrganization:
  460.                 DlgGetCString(dlg, item, organization);
  461.                 break;
  462.             case kAddress:
  463.                 DlgGetCString(dlg, item, address);
  464.                 break;
  465.         }
  466.         if (item == ok) {
  467.             if (StringIsValidEmailAddress(address)) {
  468.                 break;
  469.             } else {
  470.                 StopAlertNumber(kStrBadEmailAddress);
  471.             }
  472.         } else if (item == cancel) {
  473.             break;
  474.         }
  475.     }
  476.  
  477.     err = DoClose(dlg);
  478.     if (err != noErr) return err;
  479.     if (item == ok) {
  480.         strcpy(gPrefs.fullName, fullName);
  481.         strcpy(gPrefs.organization, organization);
  482.         strcpy(gPrefs.emailAddress, address);
  483.         MyICWriteSharedPrefs(kICRealName);
  484.         MyICWriteSharedPrefs(kICOrganization);
  485.         MyICWriteSharedPrefs(kICEmail);
  486.     }
  487.     return item == ok ? noErr : userCanceledErr;
  488. }
  489.  
  490.  
  491.  
  492. /*----------------------------------------------------------------------------
  493.     SearchFolderForPrefsFileOrAlias
  494.     
  495.     Search a folder for the preferences file or an alias to the prefs file.
  496.     
  497.     Entry:    fSpec = pointer to file spec with vRefNum and parID filled in
  498.                 for the folder to be searched.
  499.  
  500.     Exit:    function result = error code.
  501.                 = fnfErr if file not found.
  502.             *fSpec = file spec of located file in folder.
  503. ----------------------------------------------------------------------------*/
  504.  
  505. static OSErr SearchFolderForPrefsFileOrAlias (FSSpec *fSpec)
  506. {
  507.     short index = 1;
  508.     OSErr err = noErr;
  509.     Str255 oldName, reallyOldName;
  510.     FSSpec fSpec2;
  511.     FInfo fndrInfo;
  512.     
  513.     GetPString(kStrPrefsFileName, fSpec->name);
  514.     err = FSpGetFInfo(fSpec, &fndrInfo);
  515.     if (err == noErr && fndrInfo.fdCreator == kNewsWatcherSignature &&
  516.         fndrInfo.fdType == kPrefsFileType) return noErr;
  517.     err = SearchFolderByCreatorAndType(fSpec, kNewsWatcherSignature,
  518.         kPrefsFileType, &index);
  519.     if (err != noErr) return err;
  520.     GetPString(kStrOldPrefsFileName, oldName);
  521.     GetPString(kStrReallyOldPrefsFileName, reallyOldName);
  522.     if (EqualString(fSpec->name, oldName, false, true) ||
  523.         EqualString(fSpec->name, reallyOldName, false, true))
  524.     {
  525.         index++;
  526.         fSpec2 = *fSpec;
  527.         err = SearchFolderByCreatorAndType(&fSpec2, kNewsWatcherSignature,
  528.             kPrefsFileType, &index);
  529.         if (err == noErr) *fSpec = fSpec2;
  530.     }
  531.     return noErr;
  532. }
  533.  
  534.  
  535.  
  536. /*----------------------------------------------------------------------------
  537.     FindPrefsFileOrAlias
  538.     
  539.     Locate the preferences file or an alias to the prefs file.
  540.     
  541.     Entry:    firstEvent = pointer to initial Apple event.
  542.  
  543.     Exit:    function result = error code.
  544.                 = fnfErr if prefs file not found.
  545.             *fSpec = location of prefs file or prefs file alias.
  546. ----------------------------------------------------------------------------*/
  547.  
  548. static OSErr FindPrefsFileOrAlias (AppleEvent *firstEvent, FSSpec *fSpec)
  549. {
  550.     OSErr err = noErr;
  551.     DescType typeCode;
  552.     AEEventClass eventClass;
  553.     AEEventID eventID;
  554.     AEKeyword keywd;
  555.     Size actualSize;
  556.     AEDescList docList = {0, nil};
  557.     DescType returnedType;
  558.     long numItems, i;
  559.     FInfo fndrInfo;
  560.     FSSpec oldFile, prefsFolder;
  561.     CInfoPBRec pb;
  562.     
  563.     /* Check to see if NewsWatcher was opened with an ODOC or PDOC event.
  564.        If it was, scan the document list and find the first document which
  565.        is either a NewsWatcher prefs file or a saved user group list or
  566.        message file. If a prefs file is found, use that one. If a saved
  567.        document is found, check to see if a prefs file exists in the same
  568.        folder and use it if one is found. */
  569.        
  570.     err = AEGetAttributePtr(firstEvent, keyEventClassAttr, typeWildCard,
  571.         &typeCode, &eventClass, sizeof(eventClass), &actualSize);
  572.     if (err != noErr) goto exit;
  573.     if (actualSize == sizeof(eventClass) && eventClass == kCoreEventClass) {
  574.         err = AEGetAttributePtr(firstEvent, keyEventIDAttr, typeWildCard,
  575.             &typeCode, &eventID, sizeof(eventID), &actualSize);
  576.         if (err != noErr) goto exit;
  577.         if (actualSize == sizeof(eventID) && 
  578.             (eventID == kAEOpenDocuments || eventID == kAEPrintDocuments))
  579.         {
  580.             err = AEGetParamDesc(firstEvent, keyDirectObject, typeAEList, &docList);
  581.             if (err != noErr) goto exit;
  582.             err = AECountItems(&docList, &numItems);
  583.             if (err != noErr) goto exit;
  584.             for (i = 1; i <= numItems; i++) {
  585.                 err = AEGetNthPtr(&docList, i, typeFSS, &keywd, &returnedType,
  586.                     (Ptr)fSpec, sizeof(FSSpec), &actualSize);
  587.                 if (err != noErr) goto exit;
  588.                 err = FSpGetFInfo(fSpec, &fndrInfo);
  589.                 if (err != noErr) goto exit;
  590.                 if (fndrInfo.fdCreator != kNewsWatcherSignature) continue;
  591.                 if (fndrInfo.fdType == kPrefsFileType) goto exit;
  592.                 err = SearchFolderForPrefsFileOrAlias(fSpec);
  593.                 if (err != fnfErr) goto exit;
  594.             }
  595.             err = AEDisposeDesc(&docList);
  596.             if (err != noErr) goto exit;
  597.             docList.dataHandle = nil;
  598.         }
  599.     }
  600.     
  601.     /* Check to see if a prefs file exits in the Preferences folder. */
  602.  
  603.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType,
  604.         kCreateFolder, &fSpec->vRefNum, &fSpec->parID);
  605.     if (err != noErr) return err;
  606.     err = SearchFolderForPrefsFileOrAlias(fSpec);
  607.     if (err == noErr) return noErr;
  608.     if (err != fnfErr) return err;
  609.     
  610.     /* Check to see if a prefs file exists in the System folder. If it does, 
  611.        move it to the Preferences folder. */
  612.        
  613.     err = FindFolder(kOnSystemDisk, kSystemFolderType,
  614.         kCreateFolder, &oldFile.vRefNum, &oldFile.parID);
  615.     if (err != noErr) return err;
  616.     err = SearchFolderForPrefsFileOrAlias(&oldFile);
  617.     if (err != noErr) return err;
  618.     pb.dirInfo.ioNamePtr = prefsFolder.name;
  619.     pb.dirInfo.ioVRefNum = fSpec->vRefNum;
  620.     pb.dirInfo.ioFDirIndex = -1;
  621.     pb.dirInfo.ioDrDirID = fSpec->parID;
  622.     err = PBGetCatInfo(&pb, false);
  623.     if (err != noErr) return err;
  624.     prefsFolder.vRefNum = pb.dirInfo.ioVRefNum;
  625.     prefsFolder.parID = pb.dirInfo.ioDrParID;
  626.     err = FSpCatMove(&oldFile, &prefsFolder);
  627.     if (err != noErr) return err;
  628.     CopyPascalString(fSpec->name, oldFile.name);
  629.     err = FSpGetFInfo(fSpec, &fndrInfo);
  630.     if (err != noErr) return err;
  631.     fndrInfo.fdFlags &= ~(1 << 8);    /* clear hasBeenInited to force Finder to
  632.                                          assign new icon location */
  633.     return FSpSetFInfo(fSpec, &fndrInfo);
  634.     
  635. exit:
  636.  
  637.     if (docList.dataHandle != nil) AEDisposeDesc(&docList);
  638.     return err;
  639. }
  640.  
  641.  
  642.  
  643. /*----------------------------------------------------------------------------
  644.     CheckDummyPrefsFile
  645.     
  646.     Check for a dummy prefs file.
  647.     
  648.     Entry:    index = index in STR# 128 resource of dummy prefs file name.
  649.  
  650.     Exit:    function result = error code.
  651.             gDummyPrefsFile = true if dummy prefs file found.
  652.             gDummyPrefsLabFile = true if dummy prefs lab file found.
  653. ----------------------------------------------------------------------------*/
  654.  
  655. static OSErr CheckDummyPrefsFile (short index)
  656. {
  657.     OSErr err = noErr;
  658.     FSSpec fSpec;
  659.     FInfo fndrInfo;
  660.     short refNum;
  661.     long count;
  662.     char firstThreeChars[3];
  663.  
  664.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType,
  665.         kCreateFolder, &fSpec.vRefNum, &fSpec.parID);
  666.     if (err != noErr) return err;
  667.     GetPString(index, fSpec.name);
  668.     err = FSpGetFInfo(&fSpec, &fndrInfo);
  669.     if (err == fnfErr) return noErr;
  670.     if (err != noErr) return err;
  671.     gDummyPrefsFile = true;
  672.     err = FSpOpenDF(&fSpec, fsRdPerm, &refNum);
  673.     if (err != noErr) return err;
  674.     count = 3;
  675.     err = FSRead(refNum, &count, (Ptr)firstThreeChars);
  676.     if (err == noErr && count == 3 && MyStrNEqual(firstThreeChars, "lab", 3))
  677.         gDummyPrefsLabFile = true;
  678.     MyFSClose(refNum, nil);
  679.     return noErr;
  680. }
  681.  
  682.  
  683.  
  684. /*----------------------------------------------------------------------------
  685.     LocatePrefsFile
  686.     
  687.     Locate the preferences file.
  688.     
  689.     Entry:    firstEvent = pointer to initial Apple event.
  690.  
  691.     Exit:    function result = error code.
  692.                 = fnfErr if prefs file not found.
  693.             *fSpec = location of prefs file.
  694.             *prefsAlias = handle to alias record for located prefs file.
  695. ----------------------------------------------------------------------------*/
  696.  
  697. static OSErr LocatePrefsFile (AppleEvent *firstEvent, FSSpec *fSpec,
  698.     AliasHandle *prefsAlias)
  699. {
  700.     OSErr err = noErr;
  701.     Boolean targetIsFolder, wasAliased;
  702.     
  703.     /* Locate the prefs file or an alias to the prefs file */
  704.     
  705.     err = FindPrefsFileOrAlias(firstEvent, fSpec);
  706.     
  707.     /* Resolve prefs file alias. */
  708.     
  709.     if (err == noErr) {
  710.         err = ResolveAliasFile(fSpec, true, &targetIsFolder, &wasAliased);
  711.         if (err != noErr) return err;
  712.         return NewAlias(nil, fSpec, prefsAlias);
  713.     }
  714.     
  715.     /* If prefs file was not found, check to see if there is a dummy prefs
  716.        file in the Prefs folder which was created to force users of this Mac 
  717.        to use a personal prefs file. */
  718.        
  719.     if (err == fnfErr) {
  720.         err = CheckDummyPrefsFile(kStrPrefsFileName);
  721.         if (err != noErr) return err;
  722.         err = CheckDummyPrefsFile(kStrOldPrefsFileName);
  723.         if (err != noErr) return err;
  724.         err = CheckDummyPrefsFile(kStrReallyOldPrefsFileName);
  725.         if (err != noErr) return err;
  726.         return fnfErr;
  727.     }
  728.     
  729.     return err;
  730. }
  731.  
  732.  
  733.  
  734. /*----------------------------------------------------------------------------
  735.     ScramblePW
  736.     
  737.     Scramble (and unscramble) saved passwords. This is not really secure,
  738.     just something to foil people browsing using disk editors.
  739.     
  740.     Entry:    pw = the password.
  741.             len = length of password.
  742.             
  743.     Exit:    Each byte nibble-swapped and bit-flipped.
  744. ----------------------------------------------------------------------------*/
  745.  
  746. static void ScramblePW (char *pw, short len)
  747. {
  748.     char *p, *pEnd;
  749.     
  750.     pEnd = pw + len;
  751.     for (p = pw; p < pEnd; p++) *p = (((*p >> 4) & 0x0f) | ((*p & 0x0f) << 4)) ^ 0xff;
  752. }
  753.  
  754.  
  755.  
  756. /*----------------------------------------------------------------------------
  757.     ConstructSavedArtDefaultFolderAlias
  758.     
  759.     In version 2.0d9, we introduced the saved article default folder preference.
  760.     The default folder was saved on the prefs file as a volume name and dirID.
  761.     In version 2.0d41, we changed this to a saved alias resource.
  762.     
  763.     This function is called if the old prefs file version is >= 2.0d9 and < 2.0d41.
  764.     It constructs the new alias from the old volume name and dirID.
  765. ----------------------------------------------------------------------------*/
  766.  
  767. static void ConstructSavedArtDefaultFolderAlias (void)
  768. {
  769.     OSErr err = noErr;
  770.     CInfoPBRec pBlock;
  771.     FSSpec fSpec;
  772.  
  773.     if (gPrefs.savedArtDefaultFolderAlias != nil) {
  774.         MyDisposeHandle(gPrefs.savedArtDefaultFolderAlias);
  775.         gPrefs.savedArtDefaultFolderAlias = nil;
  776.     }
  777.     err = VolNameToVRefNum(gPrefs.DefunctSavedArtDefaultVolName, &fSpec.vRefNum);
  778.     if (err != noErr) return;
  779.     pBlock.dirInfo.ioVRefNum = fSpec.vRefNum;
  780.     pBlock.dirInfo.ioDrDirID = gPrefs.DefunctSavedArtDefaultDirID;
  781.     pBlock.dirInfo.ioNamePtr = fSpec.name;
  782.     pBlock.dirInfo.ioFDirIndex = -1;
  783.     err = PBGetCatInfoSync(&pBlock);
  784.     if (err != noErr) return;
  785.     fSpec.parID = pBlock.dirInfo.ioDrParID;
  786.     NewAlias(nil, &fSpec, &gPrefs.savedArtDefaultFolderAlias);
  787. }
  788.  
  789.  
  790.  
  791. /*----------------------------------------------------------------------------
  792.     ValidPString 
  793.     
  794.     Validate a saved pascal string preference.
  795.     
  796.     Entry:    str = pointer to pascal string.
  797.             size = size of pascal string.
  798.             returnIsLegal = true if return character is legal.
  799.     
  800.     Exit:    function result = true if string is valid.
  801.     
  802.     The string is considered to be "valid" if the length byte is less than
  803.     the allocated size of the string, and if all the characters in the
  804.     string are printable (or carriage returns if returnIsLegal is true).
  805. ----------------------------------------------------------------------------*/
  806.  
  807. Boolean ValidPString (StringPtr str, short size, Boolean returnIsLegal)
  808. {
  809.     short len;
  810.     
  811.     len = *(unsigned char*)str;
  812.     if (len >= size) return false;
  813.     str++;
  814.     while (len--) {
  815.         if (!isPrintable(*str)) {
  816.             if (!returnIsLegal || *str != CR) return false;
  817.         }
  818.         str++;
  819.     }
  820.     return true;
  821. }
  822.  
  823.  
  824.  
  825. /*----------------------------------------------------------------------------
  826.     ValidCString 
  827.     
  828.     Validate a saved C string preference.
  829.     
  830.     Entry:    str = pointer to C string.
  831.             size = size of C string.
  832.             returnIsLegal = true if return character is legal.
  833.     
  834.     Exit:    function result = true if string is valid.
  835.     
  836.     The string is considered to be "valid" if it contains a C-format
  837.     zero byte terminator and all the characters are printable (or 
  838.     carriage returns if returnIsLegal is true).
  839. ----------------------------------------------------------------------------*/
  840.  
  841. Boolean ValidCString (char *str, short size, Boolean returnIsLegal)
  842. {
  843.     unsigned char *p;
  844.  
  845.     p = (unsigned char*)str;
  846.     while (size--) {
  847.         if (*p == 0) {
  848.             p = (unsigned char*)str;
  849.             while (*p != 0) {
  850.                 if (!isPrintable(*p)) {
  851.                     if (!returnIsLegal || *p != CR) return false;
  852.                 }
  853.                 p++;
  854.             }
  855.             return true;
  856.         }
  857.         p++;
  858.     }
  859.     return false;
  860. }
  861.  
  862.  
  863.  
  864. /*----------------------------------------------------------------------------
  865.     PrefsAreValid 
  866.     
  867.     Validate saved preferences.
  868.     
  869.     Exit:    function result = true if prefs are valid.
  870.     
  871.     This function does some rudimentary sanity checking on the preferences.
  872. ----------------------------------------------------------------------------*/
  873.  
  874. static Boolean PrefsAreValid (void)
  875. {
  876.     return 
  877.         ValidPString(gPrefs.newsServerName, sizeof(gPrefs.newsServerName), false) &&
  878.         ValidPString(gPrefs.textFont, sizeof(gPrefs.textFont), false) &&
  879.         ValidPString(gPrefs.listFont, sizeof(gPrefs.listFont), false) &&
  880.         ValidCString(gPrefs.ftpNewsrcUsername, sizeof(gPrefs.ftpNewsrcUsername), false) &&
  881.         ValidCString(gPrefs.ftpNewsrcHost, sizeof(gPrefs.ftpNewsrcHost), false) &&
  882.         ValidCString(gPrefs.fullName, sizeof(gPrefs.fullName), false) &&
  883.         ValidCString(gPrefs.organization, sizeof(gPrefs.organization), false) &&
  884.         ValidCString(gPrefs.signature, sizeof(gPrefs.signature), true) &&
  885.         ValidPString(gPrefs.mailServerName, sizeof(gPrefs.mailServerName), false) &&
  886.         ValidCString(gPrefs.emailAddress, sizeof(gPrefs.emailAddress), false);
  887. }
  888.  
  889.  
  890.  
  891. /*----------------------------------------------------------------------------
  892.     Copy an alias 
  893.     
  894.     Entry:    a = handle to alias to be copied.
  895.             b = pointer to alias handle.
  896.     
  897.     Exit:    *b = handle to copy of alias a.
  898. ----------------------------------------------------------------------------*/
  899.  
  900. static void CopyAlias (AliasHandle a, AliasHandle *b)
  901. {
  902.     AliasHandle tmpAlias;
  903.     OSErr err = noErr;
  904.     
  905.     tmpAlias = a;
  906.     err = MyHandToHand(&tmpAlias);
  907.     if (err != noErr) return;
  908.     *b = tmpAlias;
  909. }
  910.  
  911.  
  912.  
  913. /*----------------------------------------------------------------------------
  914.     InitURLHelperInfo 
  915.     
  916.     Initialize new version 2.0b25 format URL helper info, converting the
  917.     equivalent information from any pre-version 2.0b25 prefs file, if
  918.     necessary.
  919.     
  920.     Entry:    prefsVersion = old prefs file version number.
  921.     
  922.     Exit:    function result = error code.
  923. ----------------------------------------------------------------------------*/
  924.  
  925. static OSErr InitURLHelperInfo (unsigned long prefsVersion)
  926. {
  927.     OSErr err = noErr;
  928.     TURLHelperInfo **h;
  929.  
  930.     MyDisposeHandle(gPrefs.urlHelpers);
  931.     err = MyNewHandle(10 * sizeof(TURLHelperInfo), &h);
  932.     if (err != noErr) return err;
  933.     gPrefs.urlHelpers = h;
  934.     strcpy((*h)[0].schemeName, "ftp");
  935.     strcpy((*h)[1].schemeName, "http");
  936.     strcpy((*h)[2].schemeName, "gopher");
  937.     strcpy((*h)[3].schemeName, "wais");
  938.     strcpy((*h)[4].schemeName, "telnet");
  939.     strcpy((*h)[5].schemeName, "tn3270");
  940.     strcpy((*h)[6].schemeName, "finger");
  941.     strcpy((*h)[7].schemeName, "whois");
  942.     strcpy((*h)[8].schemeName, "ph");
  943.     if (prefsVersion >= 0x02006017) {
  944.         BlockMoveData(&gPrefs.ftpHelperInfo, &(*h)[0].sig, sizeof(TOldURLHelperInfo));
  945.         BlockMoveData(&gPrefs.httpHelperInfo, &(*h)[1].sig, sizeof(TOldURLHelperInfo));
  946.         BlockMoveData(&gPrefs.gopherHelperInfo, &(*h)[2].sig, sizeof(TOldURLHelperInfo));
  947.         BlockMoveData(&gPrefs.waisHelperInfo, &(*h)[3].sig, sizeof(TOldURLHelperInfo));
  948.         BlockMoveData(&gPrefs.telnetHelperInfo, &(*h)[4].sig, sizeof(TOldURLHelperInfo));
  949.         BlockMoveData(&gPrefs.tn3270HelperInfo, &(*h)[5].sig, sizeof(TOldURLHelperInfo));
  950.         BlockMoveData(&gPrefs.fingerHelperInfo, &(*h)[6].sig, sizeof(TOldURLHelperInfo));
  951.         BlockMoveData(&gPrefs.whoisHelperInfo, &(*h)[7].sig, sizeof(TOldURLHelperInfo));
  952.     } else if (prefsVersion >= 0x02002025) {
  953.         (*h)[0].sig = gPrefs.ftpHelper;
  954.     }
  955.     if (prefsVersion >= 0x02006023) {
  956.         BlockMoveData(&gPrefs.phHelperInfo, &(*h)[8].sig, sizeof(TOldURLHelperInfo));
  957.     }
  958.     InitDefaultURLHelper("ftp");
  959.     InitDefaultURLHelper("http");
  960.     InitDefaultURLHelper("gopher");
  961.     InitDefaultURLHelper("wais");
  962.     InitDefaultURLHelper("telnet");
  963.     InitDefaultURLHelper("tn3270");
  964.     InitDefaultURLHelper("finger");
  965.     InitDefaultURLHelper("whois");
  966.     InitDefaultURLHelper("ph");
  967.     return noErr;
  968. }
  969.  
  970.  
  971.  
  972. /*----------------------------------------------------------------------------
  973.     ReadPrefs 
  974.     
  975.     Read the preferences file.
  976.     
  977.     Entry:    firstEvent = pointer to initial Apple event.
  978.     
  979.     Exit:    function result = error code.
  980. ----------------------------------------------------------------------------*/
  981.  
  982. OSErr ReadPrefs (AppleEvent *firstEvent)
  983. {
  984.     OSErr err = noErr;
  985.     FSSpec fSpec;
  986.     short fRefNum = 0;
  987.     long count, size;
  988.     unsigned long prefsVersion, progVersion;
  989.     CStr255 msg, fmt;
  990.     short len;
  991.     Handle prefsHandle;
  992.     Boolean havePrefs = false, prefsInDataFork = false, prefsDamaged = false;
  993.     Boolean prefsFileLocated;
  994.     Boolean forceServerDialog = false, forcePersonalDialog = false;
  995.     Boolean newUser = false;
  996.     DialogPtr dlg = nil;
  997.     short item;
  998.     AliasHandle nwFolderAlias = nil;
  999.     
  1000.     memset(&gPrefs, 0, sizeof(gPrefs));
  1001.     
  1002.     err = GetVersionNumber(&progVersion);
  1003.     if (err != noErr) return err;
  1004.         
  1005.     err = LocatePrefsFile(firstEvent, &fSpec, &gPrefsAlias);
  1006.     if (err != noErr && err != fnfErr) return err;
  1007.     
  1008.     prefsFileLocated = err == noErr;
  1009.     
  1010.     if (prefsFileLocated) {
  1011.         
  1012.         err = MyFSpOpenResFile(&fSpec, fsRdPerm, &fRefNum);
  1013.         if (err == noErr) {
  1014.             err = MyGet1Resource('PREF', 128, &prefsHandle);
  1015.             if (err == noErr) {
  1016.                 if (MyGetHandleSize(prefsHandle) == sizeof(TPrefRec)) {
  1017.                     havePrefs = true;
  1018.                     BlockMoveData(*prefsHandle, &gPrefs, sizeof(TPrefRec));
  1019.                     gPrefs.savedMsgDefaultFolderAlias = nil;
  1020.                     err = MyGet1Resource(rAliasType, kSavedMsgDefaultFolderAliasID, 
  1021.                         &gPrefs.savedMsgDefaultFolderAlias);
  1022.                     if (err == noErr) MyDetachResource(gPrefs.savedMsgDefaultFolderAlias);
  1023.                     gPrefs.savedArtDefaultFolderAlias = nil;
  1024.                     err = MyGet1Resource(rAliasType, kSavedArtDefaultFolderAliasID, 
  1025.                         &gPrefs.savedArtDefaultFolderAlias);
  1026.                     if (err == noErr) MyDetachResource(gPrefs.savedArtDefaultFolderAlias);
  1027.                     gPrefs.savedBinDefaultFolderAlias = nil;
  1028.                     err = MyGet1Resource(rAliasType, ksavedBinDefaultFolderAliasID, 
  1029.                         &gPrefs.savedBinDefaultFolderAlias);
  1030.                     if (err == noErr) MyDetachResource(gPrefs.savedBinDefaultFolderAlias);
  1031.                     gPrefs.savedUGLDefaultFolderAlias = nil;
  1032.                     err = MyGet1Resource(rAliasType, ksavedUGLDefaultFolderAliasID, 
  1033.                         &gPrefs.savedUGLDefaultFolderAlias);
  1034.                     if (err == noErr) MyDetachResource(gPrefs.savedUGLDefaultFolderAlias);
  1035.                     gPrefs.urlHelpers = nil;
  1036.                     err = MyGet1Resource(kURLHelpersType, kURLHelpersID, &gPrefs.urlHelpers);
  1037.                     if (err == noErr) {
  1038.                         MyDetachResource(gPrefs.urlHelpers);
  1039.                         size = MyGetHandleSize(gPrefs.urlHelpers);
  1040.                         if (size == 0 || size % sizeof(TURLHelperInfo) != 0 ||
  1041.                             *(*gPrefs.urlHelpers)[size / sizeof(TURLHelperInfo) - 1].schemeName != 0) 
  1042.                         {
  1043.                             MyDisposeHandle(gPrefs.urlHelpers);
  1044.                             gPrefs.urlHelpers = nil;
  1045.                             GetCString(kStrBadURLHelperPrefs, fmt);
  1046.                             p2cstr(fSpec.name);
  1047.                             sprintf(msg, fmt, (char*)fSpec.name);
  1048.                             c2pstr((char*)fSpec.name);
  1049.                             ErrorMessage(msg);
  1050.                         }
  1051.                     }
  1052.                     ReadArticleCache();
  1053.                 } else {
  1054.                     prefsDamaged = true;
  1055.                 }
  1056.                 ReadSavedPageSetupInfoFromPrefs();
  1057.             }
  1058.             MyCloseResFile(fRefNum);
  1059.         } 
  1060.         
  1061.         if (!havePrefs && !prefsDamaged) {
  1062.             err = FSpOpenDF(&fSpec, fsRdPerm, &fRefNum);
  1063.             if (err == noErr) {
  1064.                 count = sizeof(TPrefRec);
  1065.                 err = FSRead(fRefNum, &count, (Ptr)&gPrefs);
  1066.                 if (err == noErr && count == sizeof(TPrefRec)) {
  1067.                     havePrefs = true;
  1068.                     prefsInDataFork = true;
  1069.                     gPrefs.savedMsgDefaultFolderAlias = nil;
  1070.                     gPrefs.savedArtDefaultFolderAlias = nil;
  1071.                     gPrefs.savedBinDefaultFolderAlias = nil;
  1072.                     gPrefs.savedUGLDefaultFolderAlias = nil;
  1073.                     gPrefs.urlHelpers = nil;
  1074.                 } else {
  1075.                     prefsDamaged = true;
  1076.                 }
  1077.                 MyFSClose(fRefNum, nil);
  1078.             } else {
  1079.                 prefsDamaged = true;
  1080.             }
  1081.         }
  1082.  
  1083.         if (prefsDamaged || !havePrefs || !PrefsAreValid()) {
  1084.             havePrefs = prefsInDataFork = false;
  1085.             MyDisposeHandle(gPrefs.savedMsgDefaultFolderAlias);
  1086.             MyDisposeHandle(gPrefs.savedArtDefaultFolderAlias);
  1087.             MyDisposeHandle(gPrefs.savedBinDefaultFolderAlias);
  1088.             MyDisposeHandle(gPrefs.savedUGLDefaultFolderAlias);
  1089.             MyDisposeHandle(gPrefs.urlHelpers);
  1090.             GetCString(kStrBadPrefs, fmt);
  1091.             p2cstr(fSpec.name);
  1092.             sprintf(msg, fmt, (char*)fSpec.name);
  1093.             c2pstr((char*)fSpec.name);
  1094.             ErrorMessage(msg);
  1095.         }
  1096.     }
  1097.     
  1098.     if (gPrefs.useInternetConfig) {
  1099.         if (prefsFileLocated) {
  1100.             MyICStart(true, fSpec.vRefNum, fSpec.parID);
  1101.         } else {
  1102.             MyICStart(false, 0, 0);
  1103.         }
  1104.     }
  1105.     
  1106.     if (havePrefs) gPrefsFileFoundAtStartup = true;
  1107.     
  1108.     if (!havePrefs) {
  1109.         newUser = true;
  1110.         err = DoNewUserDialog(&nwFolderAlias);
  1111.         if (err != noErr) {
  1112.             ReportSystemError(err);
  1113.             ExitToShell();
  1114.         }
  1115.         err = MyGet1Resource('PREF', 128, &prefsHandle);
  1116.         if (err == noErr && MyGetHandleSize(prefsHandle) == sizeof(TPrefRec)) {
  1117.             BlockMoveData(*prefsHandle, &gPrefs, sizeof(TPrefRec));
  1118.             havePrefs = true;
  1119.             gPrefs.savedMsgDefaultFolderAlias = nil;
  1120.             gPrefs.savedArtDefaultFolderAlias = nil;
  1121.             gPrefs.savedBinDefaultFolderAlias = nil;
  1122.             gPrefs.savedUGLDefaultFolderAlias = nil;
  1123.             gPrefs.urlHelpers = nil;
  1124.             err = MyGet1Resource(kURLHelpersType, kURLHelpersID, &gPrefs.urlHelpers);
  1125.             if (err == noErr) MyDetachResource(gPrefs.urlHelpers);
  1126.         }
  1127.         if (prefsHandle != nil) MyReleaseResource(prefsHandle);
  1128.     }
  1129.  
  1130.     if (!havePrefs) {
  1131.         prefsVersion = 0x00000000;
  1132.         memset(&gPrefs, 0, sizeof(gPrefs));
  1133.     } else if (strcmp(gPrefs.magicCookie, "MagicCookie") != 0) {
  1134.         prefsVersion = 0x01028000;
  1135.     } else {
  1136.         len = strlen(gPrefs.versionString);
  1137.         if (len == 0) {
  1138.             prefsVersion = gPrefs.version;
  1139.         } else if (strcmp(gPrefs.versionString, "1.2(NU)") == 0) {
  1140.             prefsVersion = 0x01208000;
  1141.         } else if (len == 5 && strncmp(gPrefs.versionString, "1.3d", 4) == 0) {
  1142.             prefsVersion = 0x01302000 + gPrefs.versionString[4] - '0';
  1143.         } else if (len == 5 && strncmp(gPrefs.versionString, "2.0d", 4) == 0) {
  1144.             prefsVersion = 0x02002000 + gPrefs.versionString[4] - '0';
  1145.         } else if (len == 6 && strncmp(gPrefs.versionString, "2.0d1", 5) == 0) {
  1146.             prefsVersion = 0x02002010 + gPrefs.versionString[5] - '0';
  1147.         } else {
  1148.             prefsVersion = 0x00000000;
  1149.         }
  1150.     }
  1151.     
  1152.     strcpy(gPrefs.magicCookie, "MagicCookie");
  1153.     *gPrefs.versionString = 0;
  1154.     if (progVersion > gPrefs.version) gPrefs.version = progVersion;
  1155.     
  1156.     ScramblePW(gPrefs.ftpNewsrcPassword, sizeof(gPrefs.ftpNewsrcPassword));
  1157.     ScramblePW(gPrefs.authPassword, sizeof(gPrefs.authPassword));
  1158.     
  1159.     if (prefsVersion < 0x01028000) {
  1160.         *gPrefs.newsServerName = 0;
  1161.         *gPrefs.mailServerName = 0;
  1162.         *gPrefs.ftpNewsrcUsername = 0;
  1163.         *gPrefs.ftpNewsrcHost = 0;
  1164.         *gPrefs.fullName = 0;
  1165.         *gPrefs.organization = 0;
  1166.         *gPrefs.signature = 0;
  1167.         *gPrefs.emailAddress = 0;
  1168.         CopyPascalString(gPrefs.listFont, "\pMonaco");
  1169.         CopyPascalString(gPrefs.textFont, "\pMonaco");
  1170.         gPrefs.listSize = 9;
  1171.         gPrefs.textSize = 9;
  1172.         SetPt(&gPrefs.statusWindowLocn, 0, 0);
  1173.         gPrefs.fullGroupListVisible = true;
  1174.         gPrefs.maxFetch = kMaxMaxFetch;
  1175.     }
  1176.     
  1177.     if (prefsVersion < 0x01208000) {
  1178.         gPrefs.areYouSureAlert = true;
  1179.     }
  1180.     
  1181.     if (prefsVersion < 0x01302001) {
  1182.         gPrefs.autoFetchNewsrc = false;
  1183.     }
  1184.     
  1185.     if (prefsVersion < 0x01302004) {
  1186.         gPrefs.showArtHeaders = false;
  1187.         gPrefs.showAuthors = true;
  1188.         gPrefs.showThreadsCollapsed = true;
  1189.     }
  1190.     
  1191.     if (prefsVersion < 0x02002009) {
  1192.         gPrefs.checkForNewGroups = true;
  1193.         gPrefs.groupCheckTime = 0;
  1194.         memset(gPrefs.ftpNewsrcPassword, 0, sizeof(gPrefs.ftpNewsrcPassword));
  1195.         gPrefs.saveFtpNewsrcPassword = false;
  1196.         gPrefs.savedArtCreator = 'ttxt';
  1197.         GetPString(kStrDefaultTextFileOwner, gPrefs.savedArtAppName);
  1198.         gPrefs.maxGroupNameWidth = 0;
  1199.         gPrefs.useXPAT = false;
  1200.     }
  1201.     
  1202.     if (prefsVersion < 0x02002010) {
  1203.         gPrefs.savedArtDefaultFolder = false;
  1204.         GetCString(kStrDefaultNewsrcFileName, gPrefs.ftpNewsrcPath);
  1205.     }
  1206.     
  1207.     if (prefsVersion < 0x02002011) {
  1208.         gPrefs.addSigSeparatorLine = true;
  1209.     }
  1210.     
  1211.     if (prefsVersion < 0x02002012) {
  1212.         gPrefs.keypadShortcuts = false;
  1213.         gPrefs.logActionsToFile = false;
  1214.     }
  1215.     
  1216.     if (prefsVersion < 0x02002014) {
  1217.         gPrefs.batchedGroupCmds = false;
  1218.         gPrefs.noNewConnection = false;
  1219.         gPrefs.noModeReader = false;
  1220.     }
  1221.     
  1222.     if (prefsVersion < 0x02002016) {
  1223.         gPrefs.reZoomWindows = false;
  1224.         gPrefs.autoSaveOnQuit = false;
  1225.     }
  1226.     
  1227.     if (prefsVersion < 0x02002018) {
  1228.         gPrefs.replyPost = true;
  1229.         gPrefs.replyEmail = false;
  1230.         gPrefs.copySelf = false;
  1231.         gPrefs.showMsgDetails = false;
  1232.         gPrefs.tabEnabled = true;
  1233.         gPrefs.tabStops = 3;
  1234.         strcpy(gPrefs.quoteString, "> ");
  1235.         *gPrefs.extraNewsHdrLines = 0;
  1236.         *gPrefs.extraMailHdrLines = 0;
  1237.     }
  1238.     
  1239.     if (prefsVersion < 0x02002019) {
  1240.         gPrefs.addSigBlankLine = true;
  1241.     }
  1242.     
  1243.     if (prefsVersion < 0x02002021) {
  1244.         SetPt(&gPrefs.prefsLoc, 0, 0);
  1245.         SetPt(&gPrefs.hostLoc, 0, 0);
  1246.         SetPt(&gPrefs.servErrLoc, 0, 0);
  1247.         SetPt(&gPrefs.searchLoc, 0, 0);
  1248.         SetPt(&gPrefs.delGroupsLoc, 0, 0);
  1249.     }
  1250.     
  1251.     if (prefsVersion < 0x02002024) {
  1252.         gPrefs.wrapOnSend = true;
  1253.     }
  1254.     
  1255.     if (prefsVersion < 0x02002026) {
  1256.         *gPrefs.authUsername = 0;
  1257.         *gPrefs.authPassword = 0;
  1258.         gPrefs.authSavePassword = false;
  1259.         gPrefs.authAtStartup = false;
  1260.         SetPt(&gPrefs.authLoc, 0, 0);
  1261.     }
  1262.     
  1263.     if (prefsVersion < 0x02002027) {
  1264.         gPrefs.authPrivateGroups = false;
  1265.     }
  1266.     
  1267.     if (prefsVersion < 0x02002041) {
  1268.         gPrefs.savedMsgDefaultFolder = false;
  1269.         gPrefs.savedMsgDelAfterSend = false;
  1270.         gPrefsFileWasOldVersion = true;
  1271.         if (prefsVersion >= 0x02002009) ConstructSavedArtDefaultFolderAlias();
  1272.         gPrefs.savedBinDefaultFolder = false;
  1273.         gPrefs.hqxHelper = kExpanderCreatorType;
  1274.         err = FindAppNameFromSig(kExpanderCreatorType, gPrefs.hqxHelperName);
  1275.         if (err != noErr) GetPString(kStrHqxDecodeHelperName, gPrefs.hqxHelperName);
  1276.     } else {
  1277.         if (*gPrefs.hqxHelperName > 31) *gPrefs.hqxHelperName = 0;
  1278.     }
  1279.     
  1280.     if (prefsVersion < 0x02002044) {
  1281.         gPrefs.uuHelper = kuuUndoCreatorType;
  1282.         err = FindAppNameFromSig(kuuUndoCreatorType, gPrefs.uuHelperName);
  1283.         if (err != noErr) GetPString(kStrUUDecodeHelperName, gPrefs.uuHelperName);
  1284.         gPrefs.keyboardShortcuts = false;
  1285.     } else {
  1286.         if (*gPrefs.uuHelperName > 31) *gPrefs.uuHelperName = 0;
  1287.     }
  1288.     
  1289.     if (prefsVersion < 0x02002048) {
  1290.         gPrefs.reuseArticleWinds = false;
  1291.     }
  1292.     
  1293.     if (prefsVersion < 0x02006004) {
  1294.         gPrefs.dontCoverFinderIcons = true;
  1295.         gPrefs.saveThreadsToSeparateFiles = false;
  1296.         gPrefs.appendIfFileAlreadyExists = false;
  1297.         gPrefs.saveEncodedText = false;
  1298.         gPrefs.subjectWindPosLocked = false;
  1299.         gPrefs.articleWindPosLocked = false;
  1300.         if (prefsVersion < 0x01208000) {
  1301.             gPrefs.fullGroupWindPos.valid = false;
  1302.         } else {
  1303.             gPrefs.fullGroupWindPos.valid = true;
  1304.             gPrefs.fullGroupWindPos.oldFormat = true;
  1305.             *(Point*)&gPrefs.fullGroupWindPos.userState = gPrefs.fullGroupWindowLocn;
  1306.         }
  1307.         if (prefsVersion < 0x02002048) {
  1308.             gPrefs.autoFetchWindPos.valid = false;
  1309.         } else {
  1310.             gPrefs.autoFetchWindPos.valid = true;
  1311.             gPrefs.autoFetchWindPos.oldFormat = true;
  1312.             *(Point*)&gPrefs.autoFetchWindPos.userState = gPrefs.autoFetchNewsrcLocn;
  1313.         }
  1314.     }
  1315.     
  1316.     if (prefsVersion < 0x02006012) {
  1317.         gPrefs.returnToSubjectWindow = true;
  1318.     }
  1319.     
  1320.     if (prefsVersion < 0x02006014) {
  1321.         gPrefs.beepAtEndOfGroup = true;
  1322.         SetPt(&gPrefs.findLoc, 0, 0);
  1323.     }
  1324.     
  1325.     if (prefsVersion < 0x02006015) {
  1326.         gPrefs.startFindAtBeginning = true;
  1327.         gPrefs.savedUGLDefaultFolder = false;
  1328.         SetPt(&gPrefs.waitForDNRLoc, 0, 0);
  1329.     }
  1330.     
  1331.     if (prefsVersion < 0x02006021){
  1332.         CopyPascalString(gPrefs.printingFont, gPrefs.textFont);
  1333.         gPrefs.printingSize = gPrefs.textSize;
  1334.     }
  1335.     
  1336.     if (prefsVersion < 0x02006022) {
  1337.         gPrefs.useInternetConfig = false;
  1338.     }
  1339.     
  1340.     if (prefsVersion < 0x02006023) {
  1341.         gPrefs.useWebHelperForHtmlFiles = false;
  1342.     }
  1343.     
  1344.     if (prefsVersion < 0x02006025) {
  1345.         SetPt(&gPrefs.retryConnectLoc, 0, 0);
  1346.         gPrefs.showLabelsUnderIcons = true;
  1347.     }
  1348.     
  1349.     if (prefsVersion < 0x02006027) {
  1350.         if (prefsVersion < 0x02006014) {
  1351.             gPrefs.stopAtEndOfSubjectList = true;
  1352.             gPrefs.beepAtEndOfLists = true;
  1353.         } else {
  1354.             gPrefs.stopAtEndOfSubjectList = gPrefs.beepAtEndOfLists =
  1355.                 gPrefs.beepAtEndOfGroup;
  1356.         }
  1357.     }
  1358.     
  1359.     if (gPrefs.urlHelpers == nil || prefsVersion < 0x02006025) {
  1360.         err = InitURLHelperInfo(prefsVersion);
  1361.         if (err != noErr) return err;
  1362.     }
  1363.     
  1364.     if (nwFolderAlias == nil) {
  1365.         if (gPrefs.savedMsgDefaultFolderAlias == nil)
  1366.             MakeDefaultFolderAlias(&gPrefs.savedMsgDefaultFolderAlias);
  1367.         if (gPrefs.savedArtDefaultFolderAlias == nil)
  1368.             MakeDefaultFolderAlias(&gPrefs.savedArtDefaultFolderAlias);
  1369.         if (gPrefs.savedBinDefaultFolderAlias == nil)
  1370.             MakeDefaultFolderAlias(&gPrefs.savedBinDefaultFolderAlias);
  1371.         if (gPrefs.savedUGLDefaultFolderAlias == nil)
  1372.             MakeDefaultFolderAlias(&gPrefs.savedUGLDefaultFolderAlias);
  1373.     } else {
  1374.         gPrefs.savedUGLDefaultFolderAlias = nwFolderAlias;
  1375.         gPrefs.savedUGLDefaultFolder = true;
  1376.         CopyAlias(nwFolderAlias, &gPrefs.savedMsgDefaultFolderAlias);
  1377.         CopyAlias(nwFolderAlias, &gPrefs.savedArtDefaultFolderAlias);
  1378.         CopyAlias(nwFolderAlias, &gPrefs.savedBinDefaultFolderAlias);
  1379.     }
  1380.     
  1381.     if (prefsVersion < 0x02002026 && prefsVersion > 0) {
  1382.         err = MyGetNewDialog(kNew20UserAlert, ok, 0, &dlg);
  1383.         if (err != noErr) return err;
  1384.         SysBeep(0);
  1385.         MyModalDialog(dlg, gDialogFilterUPP, &item);
  1386.         err = DoClose(dlg);
  1387.         if (err != noErr) return err;
  1388.     }
  1389.     
  1390.     if (prefsVersion < 0x02106001 && prefsVersion > 0) {
  1391.         gPrefs.maxFetch = gPrefs.oldMaxFetch;
  1392.         if (gPrefs.maxFetch == 15000) gPrefs.maxFetch = kMaxMaxFetch;
  1393.     }
  1394.     
  1395.     if (gPrefs.maxFetch <= 0 || gPrefs.maxFetch > kMaxMaxFetch) 
  1396.         gPrefs.maxFetch = kMaxMaxFetch;
  1397.     
  1398.     if (newUser && nwFolderAlias == nil) {
  1399.         /* If this a new user who selected the "private" option, use Internet
  1400.            config to seed the new and mail server addresses and the personal
  1401.            information preferences. */
  1402.         if (prefsFileLocated) {
  1403.             MyICStart(true, fSpec.vRefNum, fSpec.parID);
  1404.         } else {
  1405.             MyICStart(false, 0, 0);
  1406.         }
  1407.         forceServerDialog = *gPrefs.newsServerName == 0 || *gPrefs.mailServerName == 0;
  1408.         if (*gPrefs.newsServerName == 0) MyICReadSharedPrefs(kICNNTPHost);
  1409.         if (*gPrefs.mailServerName == 0) MyICReadSharedPrefs(kICSMTPHost);
  1410.         forcePersonalDialog = *gPrefs.emailAddress == 0;
  1411.         if (*gPrefs.fullName == 0) MyICReadSharedPrefs(kICRealName);
  1412.         if (*gPrefs.organization == 0) MyICReadSharedPrefs(kICOrganization);
  1413.         if (*gPrefs.emailAddress == 0) MyICReadSharedPrefs(kICEmail);
  1414.         if (!gPrefs.useInternetConfig) MyICStop();
  1415.     }
  1416.     
  1417.     if (forceServerDialog ||
  1418.         *gPrefs.newsServerName == 0 || *gPrefs.mailServerName == 0) 
  1419.     {
  1420.         err = DoServerDialog();
  1421.         if (err != noErr) return err;
  1422.     }
  1423.     
  1424.     if (forcePersonalDialog || *gPrefs.emailAddress == 0) {
  1425.         err = DoPersonalDialog();
  1426.         if (err != noErr) return err;
  1427.     }
  1428.     
  1429.     CopyPascalString(gNewsServerAtStartup, gPrefs.newsServerName);
  1430.     
  1431.     if (havePrefs && gPrefsAlias != nil) {
  1432.         err = ReadGroupsFromPrefs(&fSpec, prefsInDataFork, prefsVersion);
  1433.         if (err != noErr) return err;
  1434.     }
  1435.     
  1436.     AdjustExtractBinariesCommand();
  1437.     
  1438.     return noErr;
  1439. }
  1440.  
  1441.  
  1442.  
  1443. /*----------------------------------------------------------------------------
  1444.     WriteGroups 
  1445.     
  1446.     Write the full group list to the preferences file.
  1447.     
  1448.     Entry:    fSpec = pointer to file spec for prefs file.
  1449.     
  1450.     Exit:    function result = error code.
  1451.     
  1452.     The group names are written as a plain text file, one group name
  1453.     per line, to the data fork of the prefs file.
  1454. ----------------------------------------------------------------------------*/
  1455.  
  1456. static OSErr WriteGroups (FSSpec *fSpec)
  1457. {
  1458.     OSErr err = noErr;
  1459.     Ptr groupBuf = nil;
  1460.     long groupBufNext;
  1461.     long i;
  1462.     short len;
  1463.     char *groupName;
  1464.     short fRefNum = 0;
  1465.     Boolean empty;
  1466.     
  1467.     /* If the user changed news servers, save an empty full group list to force the
  1468.        program to read a new full group list the next time it is run. */
  1469.     
  1470.     if (!EqualString(gPrefs.newsServerName, gNewsServerAtStartup, false, true)) {
  1471.         gNumGroups = 0;
  1472.         gFullGroupListDirty = true;
  1473.     }
  1474.     
  1475.     /* Return if the full group list was not changed since the prefs file was
  1476.        read at startup. */
  1477.     
  1478.     if (!gFullGroupListDirty) return noErr;
  1479.     
  1480.     /* Allocate the group name buffer. */
  1481.     
  1482.     err = MyNewPtrCritical(10000, &groupBuf);
  1483.     if (err != noErr) goto exit;
  1484.     groupBufNext = 0;
  1485.     
  1486.     /* Open the file. */
  1487.  
  1488.     err = OpenDataForkWriteCreateIfMissing(fSpec, kNewsWatcherSignature, 
  1489.         kPrefsFileType, smSystemScript, false, &fRefNum, &empty);
  1490.     if (err != noErr) goto exit;
  1491.     err = SetEOF(fRefNum, 0);
  1492.     if (err != noErr) goto exit;
  1493.     
  1494.     /* Write the group names in order to the file. */
  1495.     
  1496.     for (i = 0; i < gNumGroups; i++) {
  1497.         if (groupBufNext + 256 >= 10000) {
  1498.             err = MyFSWriteNoCache(fRefNum, &groupBufNext, groupBuf, nil);
  1499.             if (err != noErr) goto exit;
  1500.             groupBufNext = 0;
  1501.         }
  1502.         groupName = *gGroupNames + (*gGroupNameOffsets)[i];
  1503.         len = strlen(groupName) + 1;
  1504.         BlockMoveData(groupName, groupBuf + groupBufNext, len);
  1505.         groupBufNext += len;
  1506.         *(groupBuf + groupBufNext - 1) = CR;
  1507.     }
  1508.     
  1509.     /* Flush the last buffer. */
  1510.     
  1511.     if (groupBufNext > 0) {
  1512.         err = MyFSWriteNoCache(fRefNum, &groupBufNext, groupBuf, nil);
  1513.         if (err != noErr) goto exit;
  1514.     }
  1515.     
  1516.     /* Close the file */
  1517.     
  1518.     MyFSClose(fRefNum, nil);
  1519.     MyDisposePtr(groupBuf);
  1520.     return noErr;
  1521.  
  1522. exit:
  1523.  
  1524.     if (fRefNum != 0) MyFSClose(fRefNum, nil);
  1525.     MyDisposePtr(groupBuf);
  1526.     return err;
  1527. }
  1528.  
  1529.  
  1530.  
  1531. /*----------------------------------------------------------------------------
  1532.     WritePrefs 
  1533.     
  1534.     Write the preferences file.
  1535.     
  1536.     Exit:    function result = error code.
  1537. ----------------------------------------------------------------------------*/
  1538.  
  1539. OSErr WritePrefs (void)
  1540. {
  1541.     OSErr err = noErr;
  1542.     FSSpec fSpec;
  1543.     Boolean wasChanged;
  1544.     short fRefNum = 0;
  1545.     Handle h, vers = nil;
  1546.     
  1547.     if (gPrefsAlias != nil) {
  1548.         err = ResolveAlias(nil, gPrefsAlias, &fSpec, &wasChanged);
  1549.         if (err != noErr && err != fnfErr) gPrefsAlias = nil;
  1550.     }
  1551.     
  1552.     if (gPrefsAlias == nil) {
  1553.         err = FindFolder(kOnSystemDisk, kPreferencesFolderType,
  1554.             kCreateFolder, &fSpec.vRefNum, &fSpec.parID);
  1555.         if (err != noErr) return err;
  1556.         GetPString(kStrPrefsFileName, fSpec.name);
  1557.         FSpDelete(&fSpec);
  1558.         gFullGroupListDirty = true;
  1559.     } else if (gPrefsFileWasOldVersion) {
  1560.         FSpDelete(&fSpec);
  1561.         GetPString(kStrPrefsFileName, fSpec.name);
  1562.         gFullGroupListDirty = true;
  1563.     }    
  1564.     
  1565.     if (gFullGroupWindow != nil)
  1566.         SaveWindPos(gFullGroupWindow, &gPrefs.fullGroupWindPos);
  1567.  
  1568.     if (!gPrefs.saveFtpNewsrcPassword)
  1569.         memset(gPrefs.ftpNewsrcPassword, 0, sizeof(gPrefs.ftpNewsrcPassword));
  1570.     ScramblePW(gPrefs.ftpNewsrcPassword, sizeof(gPrefs.ftpNewsrcPassword));
  1571.  
  1572.     if (!gPrefs.authSavePassword)
  1573.         memset(gPrefs.authPassword, 0, sizeof(gPrefs.authPassword));
  1574.     ScramblePW(gPrefs.authPassword, sizeof(gPrefs.authPassword));
  1575.     
  1576.     MyGet1Resource('vers', 1, &vers);
  1577.     if (vers != nil) MyDetachResource(vers);
  1578.     
  1579.     err = OpenResFileWriteCreateIfMissing(&fSpec, kNewsWatcherSignature, 
  1580.         kPrefsFileType, smSystemScript, &fRefNum);
  1581.     if (err != noErr) goto exit;
  1582.     
  1583.     err = MyPtrToHand(&gPrefs, &h, sizeof(TPrefRec));
  1584.     if (err != noErr) goto exit;
  1585.     err = MyReplaceResource(h, 'PREF', 128, "\p");
  1586.     if (err != noErr) goto exit;
  1587.     
  1588.     if (vers != nil) MyReplaceResource(vers, 'vers', 1, "\p");
  1589.     
  1590.     err = WriteProgramNameResource(kStrNewsWatcher);
  1591.     if (err != noErr) goto exit;
  1592.     
  1593.     if (gPrefs.savedMsgDefaultFolderAlias != nil) {
  1594.         err = MyReplaceResource(gPrefs.savedMsgDefaultFolderAlias, rAliasType, 
  1595.             kSavedMsgDefaultFolderAliasID, "\p");
  1596.         if (err != noErr) goto exit;
  1597.     }
  1598.     
  1599.     if (gPrefs.savedArtDefaultFolderAlias != nil) {
  1600.         err = MyReplaceResource(gPrefs.savedArtDefaultFolderAlias, rAliasType, 
  1601.             kSavedArtDefaultFolderAliasID, "\p");
  1602.         if (err != noErr) goto exit;
  1603.     }
  1604.     
  1605.     if (gPrefs.savedBinDefaultFolderAlias != nil) {
  1606.         err = MyReplaceResource(gPrefs.savedBinDefaultFolderAlias, rAliasType, 
  1607.             ksavedBinDefaultFolderAliasID, "\p");
  1608.         if (err != noErr) goto exit;
  1609.     }
  1610.     
  1611.     if (gPrefs.savedUGLDefaultFolderAlias != nil) {
  1612.         err = MyReplaceResource(gPrefs.savedUGLDefaultFolderAlias, rAliasType, 
  1613.             ksavedUGLDefaultFolderAliasID, "\p");
  1614.         if (err != noErr) goto exit;
  1615.     }
  1616.     
  1617.     if (gPrefs.urlHelpers != nil) {
  1618.         err = MyReplaceResource(gPrefs.urlHelpers, kURLHelpersType,
  1619.             kURLHelpersID, "\p");
  1620.         if (err != noErr) goto exit;
  1621.     }
  1622.     
  1623.     err = WriteArticleCache(gNewsServerAtStartup);
  1624.     if (err != noErr) goto exit;
  1625.     
  1626.     WritePageSetupInfoToPrefs();
  1627.     
  1628.     MyCloseResFile(fRefNum);
  1629.     
  1630.     err = WriteGroups(&fSpec);
  1631.     if (err != noErr) goto exit;
  1632.     
  1633.     return noErr;
  1634.     
  1635. exit:
  1636.  
  1637.     if (fRefNum != 0) MyCloseResFile(fRefNum);
  1638.     return err;
  1639. }
  1640.